/* File contains the code for the accepting thread of the Beacon Nodes*/

#include "projectHeader.h"

void * beaconAccept(void * t)
{
	
		//printf("MYHostNAme is %s\n",myHostName);
		//pthread_t readBeacon[512],writeBeacon[512];
		char* myPortChar=(char*)malloc(5);
		socklen_t peerAddrAcceptLength = 0;
		sprintf(myPortChar,"%d",port);
		//strcat(buf,'\0');
		//printf("\n\nMY PORT is %s\n\n",buf);
		

		struct sockaddr_in myAddrAccept, peerAddrAccept;
		int sockfdAccept =0, clientAccept = 0, bindAccept=0;
		//int reuse_addr=1;

		//Creating Accept socket.
		sockfdAccept = socket(AF_INET, SOCK_STREAM,0);
		if(sockfdAccept<0)
		{
			//printf("\nError while creating socket...\n");
			//pthread_exit(NULL);
			//exit(1);
		}

		/*if (setsockopt(sockfdAccept, SOL_SOCKET, SO_REUSEADDR,
				(void*)(&reuse_addr), sizeof(int)) == -1) {
			 report error 
			perror("Error in setting socket option...");
			pthread_exit(NULL);
			exit(1);
		}*/

		memset(&myAddrAccept,0, sizeof(myAddrAccept));
		myAddrAccept.sin_family = AF_INET;
		myAddrAccept.sin_addr.s_addr = INADDR_ANY;
		myAddrAccept.sin_port = htons(port);
		bindAccept = bind(sockfdAccept,(struct sockaddr*)&myAddrAccept,sizeof(myAddrAccept)); // Binding server's IP Address and Port number.
		if(bindAccept<0)
		{
			//printf("\nError occured while binding...%d\n",bindAccept);
			//pthread_exit(NULL);
			//exit(1);
		}
		listen(sockfdAccept, 15);
		
		//int count = 1;
		map<string,int>::iterator it;
		while(1)
		{
			//printf("\n-------------------------Enter BEACON Accept----------------------------\n");
			peerAddrAcceptLength = sizeof(peerAddrAccept);
			memset(&peerAddrAccept,'\0',sizeof(peerAddrAccept));
			clientAccept = accept(sockfdAccept,(struct sockaddr *) &peerAddrAccept,&peerAddrAcceptLength);
			//printf("clientAccept = %d\n",clientAccept);
			if (clientAccept < 0) 
			{
			}
				//printf("ERROR on TCP accept \n");
			
			//printf("\nAccepted... %d ---> sockfdAccept = %d\n",count++,sockfdAccept);
			//printf("\nPeer port number : %d\n",);
			//printf("\nBEACON ACCEPT : peerAddrAccept Info --> port %d...\n",ntohs(peerAddrAccept.sin_port));

			//char buffer[100];
			//memset(buffer,'\0',100*sizeof(char));
			//recv(clientAccept,&buffer,sizeof(buffer),0);
			//printf("bufer = %s\n",buffer);

			//Will receieve Hello Header and then Hello Data.


			
			msgHeaderClass  beaconAcceptHeader;

			int recv_ret=(int)recv(clientAccept,&beaconAcceptHeader,sizeof(msgHeaderClass),0);
			if(recv_ret<0)
			{
				//printf("BEACON ACCEPT : Error in receieveing  Header.\n");
			}

			//Send Hello Response to the other side.


			if(beaconAcceptHeader.messageType==0xFA)
			{
				char helloDataRecvBuf[512];
				memset(helloDataRecvBuf,0,512);
				helloMsgDataClass  helloRecvData;
				//recv hello data
				recv_ret=0;
				 recv_ret=(int)recv(clientAccept,&helloDataRecvBuf,beaconAcceptHeader.msgDataLength,0);
				if(recv_ret<0)
				{
					//printf("BEACON ACCEPT : ERROR: error in receiving hello data"); 
				}
				memcpy(&helloRecvData.HostPort,helloDataRecvBuf,sizeof(helloRecvData.HostPort));
				memcpy(helloRecvData.Hostname,helloDataRecvBuf+sizeof(helloRecvData.HostPort),beaconAcceptHeader.msgDataLength-sizeof(helloRecvData.HostPort));
			
				//pthread_mutex_lock(&printMutex);
				//printf("BEACON ACCEPT : RECV Accept:messageType: %u\n",beaconAcceptHeader.messageType);
				//printf("BEACON ACCEPT : RECV Accept:");
				//printUOID(beaconAcceptHeader.UOID);
				//printf("BEACON ACCEPT : RECV Accept:ttlInHeader: %u\n",beaconAcceptHeader.ttlInHeader);
				//printf("BEACON ACCEPT : RECV Accept:HostPort: %d\n",helloRecvData.HostPort);
				//printf("BEACON ACCEPT : RECV Accept:Hostname: %s\n",helloRecvData.Hostname);
				//printf("\n\n");
				//pthread_mutex_unlock(&printMutex);
				
pthread_mutex_lock(&logFileMutex);
//
gettimeofday(&tv, NULL); 
int sec = tv.tv_sec ;
int msec = tv.tv_usec/1000;
char *time = (char *)malloc(14);
memset(time,0,14);
sprintf(time,"%10ld.%03d",(long)sec,msec);
time[strlen(time)]='\0';
fprintf(logFilePtr, "r  %s %s:%hu HLLO %d 0x%02x %x%x%x%x %hu %s\n",time,
	helloRecvData.Hostname,
	helloRecvData.HostPort,
	(beaconAcceptHeader.msgDataLength+27),
	(unsigned char)beaconAcceptHeader.ttlInHeader,
	(unsigned char)beaconAcceptHeader.UOID[16],
	(unsigned char)beaconAcceptHeader.UOID[17],
	(unsigned char)beaconAcceptHeader.UOID[18],
	(unsigned char)beaconAcceptHeader.UOID[19],
	helloRecvData.HostPort,helloRecvData.Hostname);
pthread_mutex_unlock(&logFileMutex);















				//printf("BEACON ACCEPT : Waiting for lock\n");
				pthread_mutex_lock(&connectedNodeListMutex);
				//printf("BEACON ACCEPT : got the lock\n");
				char* hostPortNumberConvert = (char*)malloc(5*sizeof(char));
				sprintf(hostPortNumberConvert,"%d",helloRecvData.HostPort);
				char* connectedNodeListKey = (char*)malloc(strlen(helloRecvData.Hostname)+strlen(hostPortNumberConvert)+2);
				
				memset(connectedNodeListKey,'\0',(strlen(connectedNodeListKey)));
				sprintf(connectedNodeListKey,"%s_%s",helloRecvData.Hostname,hostPortNumberConvert);
				//connectedNodeListKey[strlen(peerInfo->hostName)+strlen(peerInfo->portNum)+!] = '\0';
				//printf("\nBEACON ACCEPT : connectedNodeListKey = %s ----> %d\n",connectedNodeListKey,strlen(connectedNodeListKey));
				bool tieBreakerFlag=false;
			///	pthread_mutex_lock(&printMutex);
				//	for ( it=connectedNodeList.begin() ; it != connectedNodeList.end(); it++ )
						//cout << (*it).first << " => " << (*it).second << endl;
				//cout<<"##################################################"<<endl;
					//pthread_mutex_unlock(&printMutex);

				if(connectedNodeList.empty() || connectedNodeList.find(connectedNodeListKey) == connectedNodeList.end())
				{
					//printf("\nBEACON ACCEPT :---- Map is empty or key not found... So Connect...%d\n",clientAccept);
					//insertMapFlag = true;
					connectedNodeList[connectedNodeListKey]=clientAccept;
					//printf("\n%d ----> %d\n",connectedNodeList.find(connectedNodeListKey)->first,connectedNodeList.find(connectedNodeListKey)->second);
					//printf("\n%d ----> %d\n",connectedNodeList.find(connectedNodeListKey)->first,connectedNodeList.find(connectedNodeListKey)->second);
					//cout<<"BEACON ACCEPT :---- map size = "<<connectedNodeList.size()<<endl;
					tieBreakerFlag=true;
					
				}
				else
				{
					
					//cout<<"*******************************BEACON ACCEPT : map size = "<<connectedNodeList.size()<<endl;						
					//printf("\nConnection Already established... End Connect Thread\n\n");
				//	pthread_mutex_lock(&printMutex);
				//	for ( it=connectedNodeList.begin() ; it != connectedNodeList.end(); it++ )
						//cout << (*it).first << " => " << (*it).second << endl;
				//	pthread_mutex_unlock(&printMutex);


					/*check the tie breaker on basis of the port numbers of both the peers.
					If PA > PB, you should keep the connection initiated by A(me).
					Let HA and HB be the hostnames (ASCII string) of nodes A and B, respectively.
					If  strcmp(HA, HB) > 0
					you should keep the connection initiated by A. 
					If  strcmp(HA, HB) < 0 you should keep the connection initiated by B
					*/
					
					//close(clientAccept);
					

					if(helloRecvData.HostPort>port)
					{
						tieBreakerFlag=true;	// Tie Broken via port numbers
						
						//I have to accept connection....
						//Close connection of my connect thread...
						//Remove previous connection descriptor from my map...
						//Add current connection socket descriptor into map...

					}
					else if(helloRecvData.HostPort<port)
					{
						tieBreakerFlag=false; // I have to close my connection.
					}
					else // Port Numbers Same. So Break Tie by HostName now.
					{
						if(strcmp(helloRecvData.Hostname, myHostName)>=0)
						{
							tieBreakerFlag=true;	// Tie Broken via hostname.

							//I have to accept connection....
							//Close connection of my connect thread...
							//Remove previous connection descriptor from my map...
							//Add current connection socket descriptor into map...
						}
						else
						{
							tieBreakerFlag=false;
						}
					}
					//continue;
				}

				pthread_mutex_unlock(&connectedNodeListMutex);
				
				
				
				
				
				
				/*if(helloRecvData.HostPort>port)
				{
					tieBreakerFlag=true;	// Tie Broken via port numbers
				}
				else if(helloRecvData.HostPort<port)
				{
					tieBreakerFlag=false;
				}
				else // Port Numbers Same. So Break Tie by HostName now.
				{
					if(strcmp(helloRecvData.Hostname, myHostName)>=0)
					{
						tieBreakerFlag=true;	// Tie Broken via hostname.
					}
					else
					{
						tieBreakerFlag=false;
					}
				}
				*/
				//tieBreakerFlag = true;
				if(tieBreakerFlag)
				{
						//printf("BEACON ACCEPT : Hello Msg detected. Send Response.\n");
						//Send Hello Response to the other side.

							helloMsgDataClass helloSendData;
							msgHeaderClass  helloSendHeader;
							helloSendData.HostPort=port;
							strncpy(helloSendData.Hostname,myHostName,strlen(myHostName));


							unsigned char *buff1;
							char* msg = (char*)"HLLO";

							//printf("BEACON ACCEPT : NodeID is %s\n",nodeInstanceId);
							helloSendHeader.messageType=0xFA;
							buff1=GetUOID((char *)nodeInstanceId,msg,buf, sizeof(buf));
							memcpy(helloSendHeader.UOID,buf,20);
							helloSendHeader.ttlInHeader=1;
							helloSendHeader.msgDataLength=sizeof(helloSendData.HostPort)+strlen(helloSendData.Hostname);
						//	printf("\n~~~~~~~~~~~~~~~~~~~~~~~1~~~~~~~~~~~~~~~~~~~~~~~~~~\n");


							if (send(clientAccept,&helloSendHeader,sizeof(msgHeaderClass), 0) == -1)
							{
								//printf("BEACON ACCEPT : ERROR: error in sending Hello Header.\n ");
							}
						//	printf("\n~~~~~~~~~~~~~~~~~~~~~~~2~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
							char helloDataSendBuf[512];
							memset(helloDataSendBuf,0,512);
							
							memcpy(helloDataSendBuf,&helloSendData.HostPort ,sizeof(helloSendData.HostPort));
							memcpy((helloDataSendBuf+sizeof(helloSendData.HostPort)),&helloSendData.Hostname ,strlen(helloSendData.Hostname));
							
							if (send(clientAccept,helloDataSendBuf,helloSendHeader.msgDataLength, 0) == -1)
							{
								//printf("BEACON ACCEPT : ERROR: error in sending Hello Data.\n\n");
							}
					//		printf("\n~~~~~~~~~~~~~~~~~~~~~~~3~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
							//pthread_mutex_lock(&printMutex);
							//printf("BEACON ACCEPT : SEND Accept:messageType: %u\n",helloSendHeader.messageType);
							//printf("BEACON ACCEPT : SEND Accept:");
							//printf("\n~~~~~~~~~~~~~~~~~~~~~~~4~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
							//printUOID(helloSendHeader.UOID);
							//printUOID(buf);
							//printf("BEACON ACCEPT : SEND Accept:ttlInHeader: %u\n",helloSendHeader.ttlInHeader);
							//printf("BEACON ACCEPT : SEND Accept:HostPort: %d\n",helloSendData.HostPort);
							//printf("BEACON ACCEPT : SEND Accept:Hostname: %s\n",helloSendData.Hostname);
							//printf("\n\n");
						//printf("\nBEACON ACCEPT : AFTER SEND ACCEPT:Socket Descriptor = %d\n",clientAccept);
						//pthread_mutex_unlock(&printMutex);
						
pthread_mutex_lock(&logFileMutex);
//
gettimeofday(&tv, NULL); 
int sec = tv.tv_sec ;
int msec = tv.tv_usec/1000;
char *time = (char *)malloc(14);
memset(time,0,14);
sprintf(time,"%10ld.%03d",(long)sec,msec);
time[strlen(time)]='\0';
fprintf(logFilePtr, "s  %s %s:%hu HLLO %d 0x%02x %x%x%x%x %hu %s\n",time,
	helloRecvData.Hostname,
	helloRecvData.HostPort,
	(helloSendHeader.msgDataLength+27),
	(unsigned char)helloSendHeader.ttlInHeader,
	(unsigned char)helloSendHeader.UOID[16],
	(unsigned char)helloSendHeader.UOID[17],
	(unsigned char)helloSendHeader.UOID[18],
	(unsigned char)helloSendHeader.UOID[19],
	helloSendData.HostPort,helloSendData.Hostname);
pthread_mutex_unlock(&logFileMutex);





						/************************************************************************************************
						*	Create Read and write thread to handle the request on that connection.
						*************************************************************************************************/
						//Find a index in bitset and add (sockfd,index) to map. USE PROPER MUTEX
					//	printf("\nCreate read write thread...\n");
							pthread_mutex_lock(&sockIndexMapMutex);
							pthread_mutex_lock(&wQBitsetMutex);
			
							int index= findBitIndex();

							
							if(index!= -1)
							{
								//printf("BEACON ACCEPT : SocketDesc: %d is mapped to %d.",clientAccept,index);
								sockFDToQIndexMap.insert( pair<int,int>(clientAccept,index));
								pthread_mutex_lock(&keepAliveMapMutex);
								keepAliveTimers.insert(pair<int,int>(index,keepAliveTimeOut));
								pthread_mutex_unlock(&keepAliveMapMutex);
							}
							else
							{
								//printf("BEACON ACCEPT : ERROR: MAX number of read-write Thread pairs .");
								pthread_exit(NULL);
							}
							
						pthread_create(&readBeacon[index], NULL,beaconRead,(void*)clientAccept);
						pthread_create(&writeBeacon[index], NULL,beaconWrite,(void*)clientAccept);
						pthread_mutex_unlock(&wQBitsetMutex);
						pthread_mutex_unlock(&sockIndexMapMutex);

						//pthread_create(&readBeacon[acceptCounter], NULL,beaconRead,(void*)clientAccept);
						//pthread_create(&writeBeacon[acceptCounter], NULL,beaconWrite,(void*)clientAccept);
						
				}
		//		else
		//		{
		//			close(clientAccept);
					
		//		}



			}//end if for hello msg type detection
			else if(beaconAcceptHeader.messageType==0xFC)
			{
			//	printf("BEACON ACCEPT : Join Msg Recieved.\n");
				
				char joinReqDataRecvBuf[512];
				memset(joinReqDataRecvBuf,0,512);
				joinReqMsgDataClass  joinreqRecvData;
				//recv hello data
				recv_ret=0;
				recv_ret=(int)recv(clientAccept,&joinReqDataRecvBuf,beaconAcceptHeader.msgDataLength,0);
				if(recv<=0)
				{
					//printf("BEACON ACCEPT : ERROR: error in receiving join data"); 
				}

				
				
				memcpy(&joinreqRecvData.HostLocation,joinReqDataRecvBuf,sizeof(joinreqRecvData.HostLocation));
				memcpy(&joinreqRecvData.HostPort,joinReqDataRecvBuf+sizeof(joinreqRecvData.HostLocation),sizeof(joinreqRecvData.HostPort));
				memcpy(&joinreqRecvData.Hostname,joinReqDataRecvBuf+sizeof(joinreqRecvData.HostLocation)+sizeof(joinreqRecvData.HostPort),beaconAcceptHeader.msgDataLength-sizeof(joinreqRecvData.HostPort)-sizeof(joinreqRecvData.HostLocation));
			
				//pthread_mutex_lock(&printMutex);
				//printf("BEACON ACCEPT : RECV Accept:messageType: %u\n",beaconAcceptHeader.messageType);
				//printf("BEACON ACCEPT : RECV Accept:");
				//printUOID(beaconAcceptHeader.UOID);
				//printf("BEACON ACCEPT : RECV Accept:ttlInHeader: %u\n",beaconAcceptHeader.ttlInHeader);
				//printf("BEACON ACCEPT : SEND NBJoinConnect:HostLocation: %lu",joinreqRecvData.HostLocation);
				//printf("BEACON ACCEPT : RECV Accept:HostPort: %d\n",joinreqRecvData.HostPort);
				//printf("BEACON ACCEPT : RECV Accept:Hostname: %s\n",joinreqRecvData.Hostname);
				//printf("\n\n");
			//	pthread_mutex_unlock(&printMutex);
				

				pthread_mutex_lock(&connectedNodeListMutex);
				char* hostPortNumberConvert = (char*)malloc(5*sizeof(char));
				sprintf(hostPortNumberConvert,"%d",joinreqRecvData.HostPort);
				char* connectedNodeListKey = (char*)malloc(strlen(joinreqRecvData.Hostname)+strlen(hostPortNumberConvert)+2);
				
				memset(connectedNodeListKey,'\0',(strlen(connectedNodeListKey)));
				sprintf(connectedNodeListKey,"%s_%s",joinreqRecvData.Hostname,hostPortNumberConvert);
				connectedNodeList[connectedNodeListKey]=clientAccept;
				pthread_mutex_unlock(&connectedNodeListMutex);
				
				//Find a index in bitset and add (sockfd,index) to map. USE PROPER MUTEX
				pthread_mutex_lock(&sockIndexMapMutex);
				pthread_mutex_lock(&wQBitsetMutex);
			
					int index= findBitIndex();

					
					
					if(index!= -1)
					{
						////printf("BEACON ACCEPT : SocketDesc: %d is mapped to %d.",clientAccept,index);
						sockFDToQIndexMap.insert( pair<int,int>(clientAccept,index));
						pthread_mutex_lock(&keepAliveMapMutex);
						keepAliveTimers.insert(pair<int,int>(index,keepAliveTimeOut));
						pthread_mutex_unlock(&keepAliveMapMutex);
					}
					else
					{
						//printf("BEACON ACCEPT : ERROR: MAX number of read-write Thread pairs .");
						pthread_exit(NULL);
					}
					
				pthread_mutex_unlock(&wQBitsetMutex);
				pthread_mutex_unlock(&sockIndexMapMutex);
				/// Initatie the Join Msg Flooding by inserting join msg in eventQueue.
					
				pthread_mutex_lock(&eventQueueMutex);
				
				queueItem itemToBePushed;

				itemToBePushed.sockfd=clientAccept;
				itemToBePushed.msgHeader=beaconAcceptHeader;
				memcpy(&(itemToBePushed.dataBuf[0]),&joinReqDataRecvBuf[0],beaconAcceptHeader.msgDataLength);
				//printf("BEACON ACCEPT : Will push %s to evetQueue. Received data was %s\n\n",itemToBePushed.dataBuf,joinReqDataRecvBuf);
				eventQueue.push_back(itemToBePushed);
				//printf("BEACON ACCEPT : JoinReq Pushed inside the Queue.\n");
				pthread_cond_broadcast(&eventQueueCV);
				pthread_mutex_unlock(&eventQueueMutex);
				
				//pthread_create(&readBeacon[index], NULL,beaconRead,(void*)clientAccept);
				pthread_create(&writeBeacon[index], NULL,beaconWrite,(void*)clientAccept);
				// Create rw threads for the connection.
				//pthread_create(&readBeacon[acceptCounter], NULL,beaconRead,(void*)clientAccept);
				//pthread_create(&writeBeacon[acceptCounter], NULL,beaconWrite,(void*)clientAccept);


			}



						
				//acceptCounter++;


		}
		//pthread_join(readBeacon[[acceptCounter],NULL);
		//pthread_join(writeBeacon[acceptCounter],NULL);
		//printf("BEACON ACCEPT : End Accept Thread\n\n");
		free(myPortChar);
		pthread_exit(NULL);

}
